package com.normal.zbase.impl.http

import androidx.lifecycle.viewModelScope
import com.normal.zbase.impl.base.BaseVM
import com.normal.zbase.impl.exceptions.CODE_SUCCESS
import com.normal.zbase.impl.exceptions.CODE_TOKEN_INVALID
import com.normal.zbase.impl.exceptions.CODE_UNKNOWN
import com.normal.zbase.impl.exceptions.CloudException
import com.normal.zbase.impl.http.api.ApiLogin
import com.normal.zbase.impl.http.impl.service.imp.DeleteHttpServiceImp
import com.normal.zbase.impl.http.impl.service.imp.GetHttpServiceImp
import com.normal.zbase.impl.http.impl.service.imp.PostHttpServiceImp
import com.normal.zbase.impl.http.impl.service.imp.PutHttpServiceImp
import com.normal.zbase.impl.http.main.ApiConfig
import com.normal.zbase.impl.http.main.HttpManager
import com.normal.zbase.impl.tools.obj.LoadingObj
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.launch
import java.lang.reflect.Field


object ApiLoginFactory :
    ApiLogin by HttpManager.build().create(ApiLogin::class.java) {

    //更换主机域名的扩展，不需要更换主机的可以省略
    fun host(host: String? = ""): ApiLogin {
        return HttpManager.build(host ?: "").create(ApiLogin::class.java)
    }


//    ********以下使用接口路径调用************************

    fun post(path: String?): PostHttpServiceImp {
        return PostHttpServiceImp(path!!)
    }

    fun get(path: String?): GetHttpServiceImp {
        return GetHttpServiceImp(path!!)
    }

    fun put(path: String?): PutHttpServiceImp {
        return PutHttpServiceImp(path!!)
    }

    fun delete(path: String?): DeleteHttpServiceImp {
        return DeleteHttpServiceImp(path!!)
    }
}

/**
 * 网络请求统一请求
 */
fun BaseVM.requestApi(
    request: suspend () -> Unit,
    fail: suspend (errorMessage: CloudException) -> Unit,
    isShowLoading: Boolean = false,
) {
    viewModelScope.launch(CoroutineExceptionHandler { _, e ->
        if (isShowLoading) LoadingObj.hintLoad()
        viewModelScope.launch {
            fail.invoke(CloudException.accept(e))
        }
    }) {
        if (isShowLoading) LoadingObj.showLoad()
        request.invoke()
        if (isShowLoading) LoadingObj.hintLoad()

    }
}


/**
 * 统一结果解析
 */
inline fun <reified T> T.apiResponse(): T {
    val response = this
    try {
        val obj = response!!::class.java
        //获取父类的属性
        var fieldCode = getDeclaredField(response, ApiConfig.CODE_NAME)
        var fieldMessage = getDeclaredField(response, ApiConfig.MESSAGE_NAME)
        if (fieldCode == null) {
            //补偿-获取子类的属性
            fieldCode = obj.getDeclaredField(ApiConfig.CODE_NAME)
            fieldMessage = obj.getDeclaredField(ApiConfig.CODE_NAME)
        }
        fieldMessage?.isAccessible = true
        fieldCode?.isAccessible = true
        when (val code = fieldCode?.get(response)) {
            CODE_TOKEN_INVALID -> {//过滤token失效
                //todo 跳转到登录页
                throw CloudException(code as String, fieldMessage?.get(response) as String)
            }

            CODE_SUCCESS ->
                return response

            else ->
//                return response
                throw CloudException(code as String, fieldMessage?.get(response) as String)
        }
    } catch (e: Exception) {
        throw CloudException(code = CODE_UNKNOWN, message = e.message,e)
    }
}


/**
 * 循环向上转型, 获取对象的 DeclaredField
 * @param object : 子类对象
 * @param fieldName : 父类中的属性名
 * @return 父类中的属性对象
 */
fun getDeclaredField(`object`: Any, fieldName: String?): Field? {
    var field: Field?
    var clazz: Class<*> = `object`.javaClass
    while (clazz != Any::class.java) {
        try {
            field = clazz.getDeclaredField(fieldName)
            return field
        } catch (e: Exception) {
            //这里甚么都不要做！并且这里的异常必须这样写，不能抛出去。
            //如果这里的异常打印或者往外抛，则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
        }
        clazz = clazz.superclass
    }
    return null
}